﻿using System;
using System.Text;
using Library.Gsm.AtCommands;

namespace Library.Gsm {
	public abstract class BaseGsmCommand : IGsmCommand {
		public abstract string EndNewLine { get; }// "\r\n> ";
		public abstract string EndOk { get; }//= "\r\nOK\r\n";
		public abstract string EndError { get; }//= "\r\nERROR\r\n";
		public abstract string EndResponse { get; }// = "\r\n";
		public abstract string StartResponse { get; }// = "\r\n";


		private readonly IGsmConnection _connection;
		protected BaseGsmCommand(IGsmConnection connection) {
			_connection = connection;
		}




		public OneLineStringCollection Execute(IAtCommand atCommand) {
			var commandText = atCommand.AtCommandText;
			_connection.BeforeExecuteCommand();
			_connection.Write(commandText);

			var beforeArgs = new BeforeReadResponseEventArgs(atCommand);
			InvokeBeforeReadResponse(beforeArgs);


			var responseMess = new StringBuilder();

			string readMsg;
			do {
				string currentMessage;
				if (_connection.ReadResponseLimitTimeOut(out currentMessage)) {
					if (currentMessage.EndsWith(EndOk) || currentMessage.EndsWith(EndNewLine)) {
						responseMess.Append(currentMessage);
						// do not listen any more because end command reached
						break;
					}
					if (currentMessage.StartsWith(EndOk)) {
						responseMess.Append(EndOk);
						GsmAlert(currentMessage.Substring(EndOk.Length));
						break;
					}
					if (currentMessage.StartsWith(EndNewLine)) {
						responseMess.Append(EndNewLine);
						GsmAlert(currentMessage.Substring(EndNewLine.Length));
						break;
					}
					var ms = currentMessage.Split(new[] { EndOk }, StringSplitOptions.RemoveEmptyEntries);
					if (ms.Length == 1) {
						ms = currentMessage.Split(new[] { EndNewLine }, StringSplitOptions.RemoveEmptyEntries);
						if (ms.Length == 1) {// read message does not contain OK or NewLine
							responseMess.Append(currentMessage);
						} else if (ms.Length == 2) {
							responseMess.Append(ms[0]);
							responseMess.Append(EndNewLine);
							GsmAlert(ms[1]);
						} else {
							responseMess.Append(currentMessage);
						}
					} else if (ms.Length == 2) {
						responseMess.Append(ms[0]);
						responseMess.Append(EndOk);
						GsmAlert(ms[1]);
					} else {
						responseMess.Append(currentMessage);
					}
				} else {
					// timeout is matched but not response data read
					if (responseMess.Length > 0) {
						// recheck again
						var respMess = responseMess.ToString();
						if(respMess.EndsWith(EndOk) || respMess.EndsWith(EndNewLine)) {
							break;
						}
						var ps = respMess.Split(new[] { EndOk }, StringSplitOptions.RemoveEmptyEntries);
						if (ps.Length == 2) {
							responseMess = new StringBuilder();
							responseMess.Append(ps[0]);
							responseMess.Append(EndOk);
							GsmAlert(ps[1]);
							break;
						}
						ps = respMess.Split(new[] { EndNewLine }, StringSplitOptions.RemoveEmptyEntries);
						if (ps.Length == 2) {
							responseMess = new StringBuilder();
							responseMess.Append(ps[0]);
							responseMess.Append(EndNewLine);
							GsmAlert(ps[1]);
							break;
						}

						throw new UnknowResponseException(responseMess.ToString()) { CommandText = commandText };
					}

					throw new NonResponseException { CommandText = commandText };
				}

				readMsg = responseMess.ToString();

				if (readMsg.EndsWith(EndError)) {
					throw new SyntaxErrorException(responseMess.ToString()) { CommandText = commandText };
				}
			}

			while (_connection.IsOpen && !readMsg.EndsWith(EndOk) && !readMsg.EndsWith(EndNewLine));

			return atCommand.ParseResponse(responseMess.ToString(), this);
		}




		public IGsmConnection Connection {
			get { return _connection; }
		}

		public event BeforeReadResponseEventHandler BeforeReadResponse;

		protected virtual void InvokeBeforeReadResponse(BeforeReadResponseEventArgs e) {
			BeforeReadResponseEventHandler handler = BeforeReadResponse;
			if (handler != null) handler(this, e);
		}

		protected virtual void GsmAlert(string message) {

		}


	}
}
